﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel.Design;
using Microsoft.Xna.Framework.Graphics;
using Color = System.Drawing.Color;
using Rectangle = Microsoft.Xna.Framework.Rectangle;

namespace Brain.Windows
{
    /// <summary>
    /// Class that manages all the Graphics stuff
    /// </summary>
    public partial class GraphicsDeviceControl : UserControl
    {
        /// <summary>
        /// Graphics Device Service, equivalent to the Game1.graphics property
        /// </summary>
        public GraphicsDeviceService Service;

        /// <summary>
        /// Custom destination rectangle
        /// </summary>
        public Rectangle? DestinationRectangle;

        public GraphicsDeviceControl()
        {
            InitializeComponent();
        }

        protected override void OnCreateControl()
        {
            // Don't initialize the graphics device if we are running in the designer.
            if (!DesignMode)
            {
                Service = GraphicsDeviceService.AddRef(Handle, ClientSize.Width, ClientSize.Height);

                // Give derived classes a chance to initialize themselves.
                Initialize();
            }

            base.OnCreateControl();
        }


        protected virtual void Initialize()
        {
        }
        protected virtual void Draw()
        {
        }

        // Redraws the control in response to a WinForms paint message.
        protected override void OnPaint(PaintEventArgs e)
        {
            string beginDrawError = BeginDraw();

            if (string.IsNullOrEmpty(beginDrawError))
            {
                // Draw the control using the GraphicsDevice.
                Draw();
                EndDraw();
            }
            else
                // If BeginDraw failed, show an error message using System.Drawing.
                PaintUsingSystemDrawing(e.Graphics, beginDrawError);
        }

        protected Viewport CustomViewport;

        /// <summary>
        /// Attempts to begin drawing the control. Returns an error message string
        /// if this was not possible, which can happen if the graphics device is
        /// lost, or if we are running inside the Form designer.
        /// </summary>
        /// <returns></returns>
        private string BeginDraw()
        {
            // If we have no graphics device, we must be running in the designer.
            if (Service == null)
                return Text + "\n\n" + GetType();

            // Make sure the graphics device is big enough, and is not lost.
            string deviceResetError = HandleDeviceReset();

            if (!string.IsNullOrEmpty(deviceResetError))
                return deviceResetError;

            // Many GraphicsDeviceControl instances can be sharing the same
            // GraphicsDevice. The device backbuffer will be resized to fit the
            // largest of these controls. To avoid unwanted stretching, we set the
            // viewport to only use the top left portion of the full backbuffer.
            if (CustomViewport.Width == 0)
            {
                Viewport viewport = new Viewport();

                viewport.X = 0;
                viewport.Y = 0;

                viewport.Width = ClientSize.Width;
                viewport.Height = ClientSize.Height;

                viewport.MinDepth = 0;
                viewport.MaxDepth = 1;

                Service.GraphicsDevice.Viewport = viewport;
            }
            else
            {
                Service.GraphicsDevice.Viewport = CustomViewport;
            }

            return null;
        }

        // Ends drawing the control
        void EndDraw()
        {
            try
            {
                Rectangle sourceRectangle = new Rectangle(0, 0, ClientSize.Width, ClientSize.Height);
                Service.GraphicsDevice.Present(sourceRectangle, DestinationRectangle, this.Handle);
            }
            catch
            {
                // Present might throw if the device became lost while we were
                // drawing. The lost device will be handled by the next BeginDraw,
                // so we just swallow the exception
            }
        }

        // Checks the graphics device status,
        // making sure it is big enough for drawing the current control, and
        // that the device is not lost. Returns an error string if the device
        // could not be reset
        string HandleDeviceReset()
        {
            bool deviceNeedsReset = false;

            switch (Service.GraphicsDevice.GraphicsDeviceStatus)
            {
                case GraphicsDeviceStatus.Lost:
                    // If the graphics device is lost, we cannot use it at all.
                    return "Graphics device lost";

                case GraphicsDeviceStatus.NotReset:
                    // If device is in the not-reset state, we should try to reset it.
                    deviceNeedsReset = true;
                    break;

                default:
                    // If the device state is ok, check whether it is big enough.
                    PresentationParameters pp = Service.GraphicsDevice.PresentationParameters;

                    deviceNeedsReset = (ClientSize.Width > pp.BackBufferWidth) ||
                                       (ClientSize.Height > pp.BackBufferHeight);
                    break;
            }

            // Do we need to reset the device?
            if (deviceNeedsReset)
            {
                try
                {
                    Service.ResetDevice(ClientSize.Width, ClientSize.Height);
                }
                catch (Exception e)
                {
                    return "Graphics device reset failed\n\n" + e;
                }
            }

            return null;
        }

        // If we do not have a valid graphics device display a status message.
        protected virtual void PaintUsingSystemDrawing(Graphics graphics, string text)
        {
            graphics.Clear(Color.CornflowerBlue);

            using (Brush brush = new SolidBrush(Color.Black))
            {
                using (StringFormat format = new StringFormat())
                {
                    format.Alignment = StringAlignment.Center;
                    format.LineAlignment = StringAlignment.Center;

                    graphics.DrawString(text, Font, brush, ClientRectangle, format);
                }
            }
        }

        // Ignores WinForms paint-background messages. The default implementation
        // would cause flickering
        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
        }

    }
}
